package com.starview.platform.data.value;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import com.starview.platform.data.EvaluationException;
import com.starview.platform.data.IRecord;
import com.starview.platform.data.IValue;
import com.starview.platform.data.IValueVisitor;
import com.starview.platform.data.type.IType;
import com.starview.platform.data.type.TypeInterfaceType;
import com.starview.star.compiler.standard.StandardNames;

@SuppressWarnings("serial")
public class AnonRecord implements IRecord
{
  private final IValue els[];
  private final SortedMap<String, Integer> index;
  private final Map<String, IType> types;
  private final String label;
  private IType type; // set on first use of getType

  public AnonRecord(Map<String, IType> types, SortedMap<String, IValue> els)
  {
    this.els = els.values().toArray(new IValue[els.size()]);
    this.index = buildIndex(els);
    this.types = types;
    this.label = anonLabel(types, els.keySet());
  }

  public AnonRecord(Map<String, IType> types, SortedMap<String, Integer> index, IValue els[])
  {
    assert els.length == index.size();
    this.els = els;
    this.index = index;
    this.types = types;
    this.label = anonLabel(types, index.keySet());
  }

  @Override
  public int conIx()
  {
    return els.length;
  }

  @Override
  public String getLabel()
  {
    return label;
  }

  @Override
  public int size()
  {
    return els.length;
  }

  @Override
  public IValue getCell(int index)
  {
    return els[index];
  }

  @Override
  public IValue[] getCells()
  {
    return els;
  }

  @Override
  public void setCell(int index, IValue value) throws EvaluationException
  {
    els[index] = value;
  }

  @Override
  public IType getType()
  {
    if (type == null) {
      Map<String, IType> memberTypes = new TreeMap<>();
      for (Entry<String, Integer> entry : index.entrySet()) {
        memberTypes.put(entry.getKey(), els[entry.getValue()].getType());
      }
      type = new TypeInterfaceType(types, memberTypes);
    }
    return type;
  }

  @Override
  public void accept(IValueVisitor visitor)
  {
    visitor.visitRecord(this);
  }

  @Override
  public IValue getMember(String memberName)
  {
    return els[index.get(memberName)];
  }

  @Override
  public void setMember(String memberName, IValue value) throws EvaluationException
  {
    els[index.get(memberName)] = value;
  }

  @Override
  public String[] getMembers()
  {
    return index.keySet().toArray(new String[index.size()]);
  }

  @Override
  public IRecord copy() throws EvaluationException
  {
    return new AnonRecord(types, index, els);
  }

  @Override
  public IRecord shallowCopy() throws EvaluationException
  {
    return new AnonRecord(types, index, els);
  }

  public static String anonLabel(Map<String, IType> types, Collection<String> els)
  {
    StringBuilder bldr = new StringBuilder();
    bldr.append(StandardNames.RECORD_LABEL);
    int hash = 0;

    for (Entry<String, IType> entry : types.entrySet())
      hash = hash * 37 + entry.getKey().hashCode();
    for (String fieldName : els)
      hash = hash * 37 + fieldName.hashCode();

    bldr.append(Math.abs(hash));
    bldr.append("_");
    bldr.append(els.size());

    return bldr.toString();
  }

  private static SortedMap<String, Integer> buildIndex(SortedMap<String, IValue> els)
  {
    SortedMap<String, Integer> index = new TreeMap<>();
    int ix = 0;
    for (Entry<String, IValue> entry : els.entrySet()) {
      index.put(entry.getKey(), ix++);
    }
    return index;
  }

}
